#include "mprpcchannel.h"

/**
 * headerSize + headerStr(serviceName methodName argsSize) + agrsStr
*/
// 所有通过stub代理对象调用的rpc的方法，都会来到这里，
// 统一做rpc方法调用的数据序列化和网络发送
void MprpcChannel::CallMethod(const google::protobuf::MethodDescriptor* method,
                        google::protobuf::RpcController* controller, 
                        const google::protobuf::Message* request,
                        google::protobuf::Message* response, 
                        google::protobuf::Closure* done)
{
    const google::protobuf::ServiceDescriptor* sd = method->service();
    std::string serviceName = sd->name();
    std::string methodName = method->name();

    // 获取参数的序列化字符串长度
    uint32_t argsSize = 0;
    std::string argsStr;
    if(request->SerializeToString(&argsStr))
    {
        argsSize = argsStr.size();
    }
    else
    {
        controller->SetFailed("serialize request error!");
        return;
    }

    // 定义rpc请求的header
    mprpc::RpcHeader rpcHeader;
    rpcHeader.set_servicename(serviceName);
    rpcHeader.set_methodname(methodName);
    rpcHeader.set_argssize(argsSize);
    
    uint32_t headerSize = 0;
    std::string headerStr;
    if(rpcHeader.SerializeToString(&headerStr))
    {
        headerSize = headerStr.size();
    }
    else
    {
        controller->SetFailed("serialize rpcHeader error!");
        return;
    }

    // 组织待发送的rpc请求的字符串
    std::string sendRpcStr;
    sendRpcStr.insert(0, std::string((char*)&headerSize, 4)); // 前4个字节以二进制的形成存储
    sendRpcStr += headerStr;
    sendRpcStr += argsStr;

    // 打印调试信息
    /*
    std::cout << "==========================================" << std::endl;
    std::cout << "headerSize: " << headerSize << std::endl;
    std::cout << "headerStr: " << headerStr << std::endl;
    std::cout << "serviceName: " << serviceName << std::endl;
    std::cout << "methodName: " << methodName << std::endl;
    std::cout << "argsSize: " << argsSize << std::endl;
    std::cout << "argsStr: " << argsStr << std::endl;
    std::cout << "sendRpcStr: " << sendRpcStr << std::endl;
    std::cout << "==========================================" << std::endl;
    */

    // 使用TCP编程，完成rpc方法的远程调用
    int clientFd = socket(AF_INET, SOCK_STREAM, 0);
    if(clientFd == -1)
    {
        controller->SetFailed("create clientFd error: " + std::string(strerror(errno)));
        return;
    }

    // 读取配置文件RpcProvider的信息
    /*
    std::string ip = MprpcApplication::getInstance().getConfig().Load("rpcserverip");
    uint16_t port = atoi(MprpcApplication::getInstance().getConfig().Load("rpcserverport").c_str());
    */

    // rpc调用方想要调用serviceName的methodName的服务，需要查询在zookeeper上该服务的host信息
    ZkClient zkCli;
    zkCli.Start(); // 与zookeeper server 建立连接
    /******   /UserService/Login   ******/
    std::string methodPath = "/" + serviceName + "/" + methodName;
    /* hostData ==> 127.0.0.1:8080 */ 
    std::string hostData = zkCli.getData(methodPath.c_str());
    if(hostData == "")
    {
        controller->SetFailed(methodPath + " does not exist!");
        return;
    }
    size_t index = hostData.find(":");
    if(index == std::string::npos)
    {
        controller->SetFailed(methodPath + " address is invalid!");
        return;
    }
    std::string ip = hostData.substr(0, index);
    uint16_t port = atoi(hostData.substr(index + 1, hostData.size() - index).c_str());

    struct sockaddr_in serverAddr;
    serverAddr.sin_family = AF_INET;
    serverAddr.sin_port = htons(port);
    serverAddr.sin_addr.s_addr = inet_addr(ip.c_str());

    if(connect(clientFd, (struct sockaddr*)&serverAddr, sizeof(serverAddr)) == -1)
    {
        controller->SetFailed("connect error: " + std::string(strerror(errno)));
        close(clientFd);
        return;
    }

    // 发送rpc请求
    if(send(clientFd, sendRpcStr.c_str(), sendRpcStr.size(), 0) == -1)
    {
        controller->SetFailed("send error: " + std::string(strerror(errno)));
        close(clientFd);
        return;
    }

    // 接收rpc请求的响应
    char recvBuf[1024] = {0};
    uint32_t recvSize = 0;
    if((recvSize = recv(clientFd, recvBuf, 1024, 0)) == -1)
    {
        controller->SetFailed("recv error: " + std::string(strerror(errno)));
        close(clientFd);
        return;
    }

    // 反序列化rpc调用的响应数据
    // 由于是二进制数据提前出现了'\0'，导致使用string类型反序列化失败
    // std::string responseStr(recvBuf, 0, recvSize); 
    // if(!response->ParseFromString(responseStr))
    if(!response->ParseFromArray(recvBuf, recvSize))
    {
        controller->SetFailed("parse responseStr error: " + std::string(recvBuf));
        close(clientFd);
        return;
    }

    close(clientFd);
}